<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>配参管理</title>
        <script src="./lib/axios.js"></script>
        <script src="./lib/vue.js"></script>
        <link rel="stylesheet" href="./lib/elementindex.css">
        <script src="./lib/elementindex.js"></script>
        <script src="./lib/ajv.min.js"></script>
        <script src="./lib/ace-noconflict/ace.js"></script>

        <style>
            .el-dialog__header{
                    padding-top: 10px;
                    padding-bottom: 5px;
                }
            .el-dialog__body{
                padding-top: 0px;
            }
            .el-form-item {
                margin-bottom: 16px;/*2px. too small will overlap the error message*/
            }
            .el-form-item--small.el-form-item {
                margin-bottom: 14px;
            }
            .el-form-item__content {
                line-height: 35px;
                font-size: 14px;
            }
            .el-checkbox__label {
               font-size: 10px;
            }
            
            .hc-lang-editor {
                border: 1px solid #DCDFE6;
                border-radius: 4px;
            }
            
            .hc-lang-editor[readonly] {
                border: 1px solid #DCDFE6;
                border-radius: 4px;
                background-color: ghostwhite;
            }
            
            .hc-lang-editor:hover {
                border: 1px solid lightgray;
                border-radius: 4px;
            }
            </style>
    </head>
    <body>

        <div id='home_header' style="display: none;">
            <el-header>
                <span style="margin-left: -20px;">
                    <el-dropdown size='large' type="primary" @command="handleUserOperate">
                        <span class="el-dropdown-link">
                            <i class="el-icon-s-grid" style="font-size: medium;font-weight: bold; color: #3A8EE6;">配参工具</i>
                            <i class="el-icon-arrow-down el-icon--left"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown" style="opacity: 0.9;">
                            <el-dropdown-item command='/load.html' style="color: #3A8EE6;">查看并装载</el-dropdown-item>
                            <el-dropdown-item command='/handleDownloadJson' style="color: #3A8EE6;">导出</el-dropdown-item>
                            <el-dropdown-item command='/import' style="color: #3A8EE6;">导入</el-dropdown-item>
                            <el-dropdown-item command='/cipher' style="color: #3A8EE6;">加解密工具</el-dropdown-item>
                            <el-dropdown-item command='/inspect' style="color: #3A8EE6;">探查工具</el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>
                </span>
                &nbsp;
                <span>
                    <el-dropdown size='large' type="primary" @command="handleUserOperate">
                        <span class="el-dropdown-link">
                            <i class="el-icon-menu" style="font-size: medium;font-weight: bold; color: #3A8EE6;">配参管理</i>
                            <i class="el-icon-arrow-down el-icon--left"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown" style="opacity: 0.9;">
                            <el-dropdown-item command='/history.html' style="color: #3A8EE6;">管理变更</el-dropdown-item>
                            <el-dropdown-item command='/triggerGroupUpdate' style="color: #3A8EE6;">管理分组</el-dropdown-item>
                            <el-dropdown-item command='/triggerCreate' style="color: #3A8EE6;">新建配参</el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>
                </span>
                &nbsp;
                <span>
                    <el-dropdown size='large' type="primary" @command="handleUserOperate">
                        <span class="el-dropdown-link">
                            <i class="el-icon-s-tools" style="font-size: medium;font-weight: bold; color: #3A8EE6;">辅助功能</i>
                            <i class="el-icon-arrow-down el-icon--left"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown" style="opacity: 0.9;">
                            <el-dropdown-item v-if="/^self$|^mixture$/.test(context.securityMode) && (context.user.func & 8)===8"
                                command='/security-user-home.html' style="color: #3A8EE6;">管理用户</el-dropdown-item>
                            <el-dropdown-item command='/systeminfo.html' style="color: #3A8EE6;">环境变量</el-dropdown-item>
                            <el-dropdown-item v-if="context.updater ==='rabbitmq'" command='/application-nodes' style="color: #3A8EE6;">应用节点</el-dropdown-item>
                            <el-dropdown-item command='/user-guide' style="color: #3A8EE6;">用户手册</el-dropdown-item>
                            <el-dropdown-item command='/readme' style="color: #3A8EE6;">研发文档</el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>
                </span>

                <span>
                    <el-dropdown size='large' type="primary" @command="handleProfileSelect">
                        <span class="el-dropdown-link">
                            <span style="font-size: medium;font-weight: bold; ">环境:{{profile}}</span>
                            <i class="el-icon-arrow-down el-icon--left"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown" style="opacity: 0.9;">
                            <el-dropdown-item v-for="profile in profileList" :key="profile.code" :command='profile.code'>
                                {{profile.name===profile.code ? profile.code : profile.code + "("+profile.name+")"}}</el-dropdown-item>
                            <el-dropdown-item command='!manageProfiles!'><span style="color: #3A8EE6;">管理环境 ...</span></el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>
                </span>

                <span style="position: absolute;left:45%;font-size: large; color: lightgray ">Easy Configuration
                    Management</span>
                <span style="float: right; padding: 3px 0">
                    <span style="margin-left: 20px;">
                        <el-dropdown v-if="/^self$/.test(context.securityMode)" size='large' type="primary"
                            @command="handleUserOperate">
                            <i class="el-icon-user-solid" style="font-size: large;min-width:60px;font-weight: bold; color: #3A8EE6;">{{context.username}}</i><i
                                class="el-icon-arrow-down el-icon--left"></i>
                            <el-dropdown-menu slot="dropdown" style="opacity: 0.9;">
                                <el-dropdown-item command='/security-logout' style="color: #3A8EE6;">退出登录</el-dropdown-item>
                                <el-dropdown-item command='/security-change-password' style="color: #3A8EE6;">修改密码</el-dropdown-item>
                                <el-dropdown-item command='/security-login.html' style="color: #3A8EE6;">切换用户</el-dropdown-item>
                            </el-dropdown-menu>
                        </el-dropdown>
                    </span>
                </span>
            </el-header>
        </div>

        <script type="module" id="home_header-script">
            import * as $util from './lib/util.js'
                        
            window.vmEasyHeader = new Vue({
              el: '#home_header',
              data: {
                   context:{},
                   profile:$util.getUrlArgOrDefault('profile','default'),
                   profileConfig:{},
                   profileList:[{code:"default",name:"默认"}],
              },
              mounted:function()
              {
                  this.$el.style.display= 'block';
              },
              methods: {
                pickupProfileConfig(configList)
                {
                    this.profileConfig = configList.find(it=>it.item_code==='easyconfig.application-profiles');
                    this.profileList = JSON.parse(this.profileConfig.item_value).list;
                },
                handleProfileSelect:function(cmd)
                {
                    let self = this;
                    if(cmd ==='!manageProfiles!')
                    {
                        // axios.get(window.hosturl+"/profile-list").then(resp=>{
                        //    window.vmApplicationProfiles.showform(resp.data.data, callback);
                        // }).catch(ex=>{self.$message({showClose:true,type:"error",message:ex});});
                        window.vmApplicationProfiles.showform(self.profileConfig, callback);
                        return;
                        function callback(profileConfig)
                        {
                            profileConfig.action='updated';
                            axios.post(window.hosturl +'/change-submit',{remark:'更新环境信息',array:[profileConfig],action:"published"}).then(
                            resp =>{
                                self.$message({
                                         type:resp.data.error?'error':'success',
                                         message:resp.data.error?resp.data.data:'环境信息更新成功'
                                     });
                            }).catch(ex=>{
                                self.$message({showClose:true,type:"error",message:ex});
                            });
                            self.profileList = JSON.parse(self.profileConfig.item_value).list;
                        }
                    }
                    
                    if(this.profile === cmd) return;
                    this.profile= cmd;
                    window.vm.handleFilter();
                },
                  handleUserOperate:function (cmd)
                  {
                      let self = this;
                      switch(cmd)
                      {
                          case '/security-logout':
                               axios.get(window.hosturl+cmd).then(resp=>{
                                  document.location.href = '.' + '/security-login.html';
                               }).catch(ex=>{self.$message({showClose:true,type:"error",message:ex});});
                               
                               break;
                          case '/security-change-password':
                                window.vmSecurityPassword.showform();
                                break;
                          case '/load.html':
                          case '/security-login.html':
                          case '/history.html':
                          case '/security-user-home.html':
                          case '/systeminfo.html':
                               document.location.href = '.' + cmd + `?profile=${window.vmEasyHeader.profile}`;
                               break;
                          case '/handleDownloadJson':
                              window.vmDownload.showform(window.vm.$data,this.profile);
                              break;
                          case '/import':
                              // window.vmupload.showUploadDlg=true;
                              window.vmupload.showform({
                                  targetConfigList:window.vm.configList,
                                  targetProfileList:this.profileList,
                                  targetProfile:this.profile,
                                  currentUser: window.context.user.username,//currentUser,
                                  callback:function(selections){
                                      // console.warn("selections",selections)
                                      window.vm.handleFilter();
                                      window.vm.editedCount += selections.length;
                                  }
                                });
                              break;
                          case '/user-guide':
                              window.open('https://gitee.com/sleepywang1024/easyconfig/blob/master/user-guide.md');
                              break;
                          case '/readme':
                              window.open('https://gitee.com/sleepywang1024/easyconfig/blob/master/README.md');
                              break;
                           case '/environment':
                               window.open(window.hosturl+"/environment");
                               break;
                           case '/triggerCreate':
                               window.vm.triggerCreate();
                               break;
                           case '/triggerGroupUpdate':
                               window.vm.triggerGroupUpdate();
                               break;
                           case '/cipher':
                               window.vmcipher.showCipherForm=true;
                               break;
                            case '/inspect':
                               window.vmInspect.showform({config:{},context:window.context});
                               break;
                            case '/application-nodes':
                                window.vmApplicationNodes.showform();
                                break;

                      };
                   },
                 
              },//end of method
          });
       </script>

        <div id='app' style="display: none;">
            <el-form>
                <el-form-item style="margin-bottom: 0px;">
                    <el-row>
                        <el-col :span='16'>
                            <el-tabs type="card" v-model="activeGroup" @tab-click="handleFilter">
                                <el-tab-pane label="默认分组" name="." key="">
                                    <span slot="label"><i class="el-icon-house"></i>
                                        <span v-if="activeGroup==='.'" style="font-weight: bold;">默认分组</span>
                                        <span v-else>默认分组</span>
                                    </span>
                                </el-tab-pane>
                                <el-tab-pane v-for="group in easygroups" :key="group.prefix" :label="group.name" :name="group.prefix">
                                    <span slot="label">
                                        <span v-if="activeGroup===group.prefix" style="font-weight: bold;">{{group.name}}</span>
                                        <span v-else>{{group.name}}</span>
                                    </span>
                                </el-tab-pane>
                            </el-tabs>
                        </el-col>

                        <el-col :span='4'>
                            <el-input type="text" placeholder="输入关键字,定位配参项" v-model="currentInput" rows="1" @input="handleFilter">
                            </el-input>
                        </el-col>

                        <el-col :span='4'>&nbsp;&nbsp;
                            <el-dropdown size='medium' split-button type="primary" @command="handlePublish" @click="handlePublish('published')">
                                保存-发布
                                <el-dropdown-menu slot="dropdown">
                                    <el-dropdown-item command='planed'>保存-等待发布</el-dropdown-item>
                                    <el-dropdown-item v-if="context.updater == 'rabbitmq'" command='stored'>保存-灰度发布</el-dropdown-item>
                                    <el-dropdown-item command='published'>保存-发布(立即)</el-dropdown-item>
                                </el-dropdown-menu>
                            </el-dropdown>
                            <span v-if="editedCount>0" style="color:lightcoral;">生效前需保存</span>
                        </el-col>
                    </el-row>

                </el-form-item>

                <el-table border :data="currentList" style="width: 100%;margin-bottom: 20px; margin-top: -16px;"
                    row-key="id" @cell-dblclick='handleCellDblClick' :cell-style="{padding: 0}" v-loading="listLoading">
                    <template slot="empty">
                        <span style="font-weight: bold; font-size: 14px;">暂时没有发现满足条件的、由easyconfig管理的配参。</span>
                    </template>
                    <el-table-column label="配参代码" prop="item_code" :show-overflow-tooltip="true">
                    </el-table-column>
                    <el-table-column label="配参名称" prop="item_name" :show-overflow-tooltip="true">
                    </el-table-column>

                    <el-table-column label="配参数值" prop="item_value" :show-overflow-tooltip="true">
                        <template slot-scope="scope">
                            <el-form-item :rules="[{validator:(x,y,z)=>handleValueValidate(x,scope.row,z),trigger:'blur'}]"
                                :prop="scope.row.item_code">
                                <el-input v-if="!scope.row.item_schema.enum && editing === 'item_value-'+scope.row.id"
                                    type="textarea" rows='1' v-model.trim="scope.row.item_value" placeholder="配参数值"
                                    :ref="'item_value-'+scope.row.id" @blur="editing = null;" @keyup.enter.native="editing = null;"
                                    @change="scope.row.action='updated';editedCount++;">
                                </el-input>

                                <el-select v-if="scope.row.item_schema.enum && editing === 'item_value-'+scope.row.id"
                                    v-model.trim="scope.row.item_value" placeholder="请选择" :ref="'item_value-'+scope.row.id"
                                    style='width: 100%;' @change="scope.row.action='updated';editedCount++;editing = null;">
                                    <el-option v-for="item in scope.row.item_schema.enum" :key="item" :label="item.toString()"
                                        :value="item">
                                    </el-option>
                                </el-select>
                                <span v-if="editing !== 'item_value-'+scope.row.id">{{scope.row.item_value}}</span>
                            </el-form-item>
                        </template>

                    </el-table-column>

                    <el-table-column label="最后修改" prop="updated_info" width="160px" :show-overflow-tooltip="true">
                        <template slot-scope="scope">
                            <span>{{scope.row.updated_user + '@' + new Date(scope.row.updated_time).toLocaleDateString() + new Date(scope.row.updated_time).toLocaleTimeString()}}</span>
                        </template>
                    </el-table-column>

                    <el-table-column title="操作">
                        <template slot="header">
                            <span>操作</span>
                        </template>
                        <template slot-scope="scope">
                            <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                <el-button size='mini' icon='el-icon-edit' @click="handleEditCell('item_value-'+scope.row.id, scope.row)"></el-button>
                                <div slot="content">
                                    编辑 : 鼠标双击左侧配参数值单元格同样可以进行编辑！编辑后保存方能生效。
                                </div>
                            </el-tooltip>
                            <el-button size='mini' @click="triggerRowSetup(scope.row)">
                                <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                    <i class="el-icon-setting"></i>
                                    <div slot="content">
                                        设置：修改当前配参各类信息，方便复杂数据类型维护，以及密文存储、只读配参只能通过此处修改。
                                    </div>
                                </el-tooltip>
                            </el-button>
                            <el-button size='mini' @click="triggerRowHistory(scope.row)">
                                <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                    <i class="el-icon-view"></i>
                                    <div slot="content">
                                        历史：当前配参记录的变更历史记录，可恢复为以往历史修改前数值。
                                    </div>
                                </el-tooltip>
                            </el-button>

                            <el-button size='mini' @click="triggerInspect(scope.row)">
                                <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                    <i class="el-icon-magic-stick"></i>
                                    <div slot="content">
                                        探查及删除：查看配参的所有定义、当前生效信息、被使用的地方及数值，以及对不再管理配参的删除。
                                    </div>
                                </el-tooltip>
                            </el-button>


                        </template>
                    </el-table-column>
                </el-table>

            </el-form>

        </div>

        <script type='module' id="app-script">
            window.hosturl = document && document.location.href.startsWith('http://127.0.0.1:8848') ? 'http://localhost/resources' : '.';
      
            window.vm = new Vue({
                  el: '#app',
                  data: {
                      maxid:0,
                      configList:[],
                      currentList:[],
                      currentInput:'',
                      editing:'',
                      editedCount:0,
                      activeGroup:localStorage.getItem('home-config-active-tab')?localStorage.getItem('home-config-active-tab'):'.',
                      listLoading:false,
                      context:{},
                  },
                  mounted:function()
                  {
                      this.$el.style.display= 'block';
                  },
                  watch:{
                      "activeGroup":{
                          // immediate: true,
                          handler: function(nv, ov){
                              localStorage.setItem('home-config-active-tab',nv);
                          }
                      },
                  },
                  methods: {
                      handleFilter:function()
                      {
                          let noFilter = !this.currentInput;
                          let val  = this.currentInput;
                          // let group = this.activeGroup=='.'?'':this.activeGroup;
                          this.currentList = this.configList.filter(
                          it =>{
                              let thisgroup;
                              if(this.activeGroup==='.')
                              {
                                  thisgroup = true;
                                  let i = 0;
                                  let groups = this.easygroups;
                                  while(i < groups.length && thisgroup)
                                  {
                                      if(it.item_code.startsWith(groups[i].prefix)) {
                                          thisgroup = false;
                                      };
                                      i++;
                                  }
                              }else {
                                  thisgroup = it.item_code.startsWith(this.activeGroup);
                              }
                             let profile = window.vmEasyHeader.profile;
                              return (noFilter 
                                          || it.item_code&&it.item_code.indexOf(val) >= 0 
                                          || it.item_name&&it.item_name.indexOf(val) >= 0 
                                          || it.item_value&&it.item_value.indexOf(val) >= 0
                                     )&& it.item_profile === profile && it.action!=='deleted' && thisgroup && !it.item_schema.$hidden;
                               });
                      },
                      handleCellDblClick:function(row, column, cell, event)
                      {
                         let prop = column.property + '-' + row.id
                         this.handleEditCell(prop,row)
                      },
                      handleEditCell:function(colAndId,row)
                      {
                          if(row.item_schema.readOnly)
                          {
                              this.$message({showClose:true,type:"warning",message:'只读配参，修改前必须关闭【只读】(->设置)'});
                              return;
                          }
                         this.editing = colAndId;
                         this.$nextTick(function(){
                             let input = this.$refs[colAndId]
                             if(input)input.focus();
                         })
                      },
                      triggerInspect:function(row)
                      {
                          window.vmInspect.showform({config:row,context:window.context});
                      },
                      triggerRowSetup:function(row)
                      {
                          window.vmSetupDlg.showForm(row);
                      },
                      triggerRowHistory:function(row)
                      {
                          window.vmItemHistory.showForm(row);
                      },
                      triggerCreate:function(){
                          let row =  {
                              id:++ this.maxid,
                              item_profile:window.vmEasyHeader.profile,
                              item_code:this.activeGroup==='.'?'':this.activeGroup,
                              item_name:'',
                              item_value:'',
                              item_schema:{type:"string"},
                              item_status:0,
                              updated_time:new Date().getTime(),
                              updated_user:window.context.user.username,//window.currentUser,
                              action:'created',
                              };
                           window.vmSetupDlg.showForm(row);
                      },
                      handlePublish:function(cmd)
                       {   
                           if(this.editedCount==0)
                           {
                               this.$message({showClose:true,type:'warn',message:'没有修改的记录，无需保存发布！' });
                                return;
                           }
                           update(this,cmd);
                       },
                       handleValueValidate:function(rule,row,callback,isDefault)
                         {
                             let result = validateCfgValue(row,isDefault);
                             if(!result.valid){ 
                                 callback(new Error(result.message));
                                 this.handleEditCell("item_value-"+row.id,row);
                             }
                             else {
                                 callback();
                             }
                         },
                         triggerGroupUpdate:function()
                         {
                             let self = this;
                             window.vmgroups.showform(this.easygroups,data=>{
                                 let txt = JSON.stringify(data);
                                 let easygroup = this.configList.find(it=>it.item_profile=== 'default' && it.item_code==='easyconfig.groups');
                                 if(easygroup) {
                                     easygroup.item_value = txt;
                                     // self.handleFilter();
                                     updateGroups(easygroup)
                                  }
                                 
                             })
                         }
                  },//end of method
                  computed:{
                      easygroups:{
                          get(){
                              let group = this.configList.find(it=>it.item_profile=== 'default' && it.item_code==='easyconfig.groups');
                              group = group?JSON.parse(group.item_value):[];
                              // if(this.activeGroup != '.')
                              // {
                              //     let active = this.activeGroup;
                              //     if(!group.find(it=>it.prefix === active))
                              //     {
                              //         this.activeGroup = '.';
                              //     }
                              // }
                              return group;
                          }
                      }
                  },
              });
            
            loadData();
            //planed stored published
            function update(self,command)
            {
                let updatedlist = self.configList.filter(it=>it.action);
                let data = {
                            remark:'管理(编辑/设置/删除)配参',
                            action: command,
                            array: updatedlist,
                            };
                axios.post(window.hosturl +'/change-submit',data).then(
                resp =>{
                    if(resp.data.error=='OngoingConflictException')
                    {
                        vm.$message({
                                showClose:true,
                                 type:'error',
                                 message:`当前变更记录与其它进行中变更记录重叠，无法进行。${resp.data.data}`
                             });
                        return;
                    }else if(typeof resp.data!=='object' || resp.data.error)
                    {
                        vm.$message({
                                showClose:true,
                                 type:'error',
                                 message:resp.data.data
                             });
                        return;
                    }
                    
                    loadData();
                    
                    if(data.action=='stored')
                        window.vmGrayPublish.showform(resp.data.data.changeItem,status=>{
                            if(status ==='graying')
                                self.$message({
                                        showClose:true,
                                         type:'success',
                                         message:data.gray?'保存成功,请到配参管理->管理变更处继续灰度发布！':'保存成功'
                                     });
                        });

                            
                }).catch(ex=>{
                    vm.$message({showClose:true,type:"error",message:ex});
                });
            };
            
            function updateGroups(groups)
            {
                groups = JSON.parse(JSON.stringify(groups));
                groups.action= "updated";
                axios.post(window.hosturl +'/change-submit',{remark:'管理属性分组',array:[groups],action:"published"}).then(
                resp =>{
                    if(resp.data.error)
                    {
                        vm.$message({showClose:true,type:"error",message:resp.data.data});
                    }else loadData();
                }).catch(ex=>{
                    vm.$message({showClose:true,type:"error",message:ex});
                });
            };
            
 
           function loadData()
           {
               if(vm)vm.listLoading = true;
               axios.get(window.hosturl +'/config-list').then(resp=>{
                  if(typeof resp.data!=='object' || resp.data.error)
                  {
                      vm.$message({
                              showClose:true,
                               type:'error',
                               message:resp.data.data
                           });
                       vm.listLoading =false;
                      return;
                  }
                   window.vmEasyHeader.context = resp.data.context?resp.data.context:{};
                   window.context = window.vmEasyHeader.context;
                   vm.context = resp.data.context?resp.data.context:{};
                   let maxid = 0;
                   resp.data.data.forEach(it=>{
                           it.id = ++ maxid;
                           if(!it.item_schema) it.item_schema = {type:"string"};
                       });
                   vm.maxid = maxid;
                   vm.editedCount = 0;
                   vm.configList = resp.data.data;
                   
                   window.vmEasyHeader.pickupProfileConfig(vm.configList);
                   
                   // window.currentUser = resp.data.currentUser;
                   
                   vm.handleFilter();
                   
                   vm.listLoading =false;
                   
               }).catch(ex=>{
                   vm.listLoading =false;
                   vm.$message({showClose:true,type:"error",message:ex});
               });
               
           }
        window.loadData = loadData;
        window.validateCfgValue = function(row,isDefault)
         {
             let required = isDefault?false:row.item_schema.$required;
             
             let value = isDefault?row.item_schema.default:row.item_value;
             
             if(value==null || typeof(value)==='string' && value.trim()==='')
                if(required) return {valid:false,message:'config item is required'} 
                else return {valid:true};
             
             try{
                 if(row.item_schema.type !=='string')
                    value = JSON.parse(value);
             }
             catch(e)
             {
                 return {valid:false,message:`should be ${row.item_schema.type}` };
             }
             
             const ajv = new Ajv();
             const validate = ajv.compile(row.item_schema);
             if (!validate(value))
              {
                  let errors = validate.errors;
                 return {valid:false,message:errors.map(it=>it.dataPath + ' '+ it.message).join("\n")};
              }
              
            return {valid:true,message:`${row.item_code} is valid`}
         }
        </script>

        <div id='setupDlg' style="display: none ;">
            <el-dialog max-height="400px" :visible.sync="showSetupDlg" :title="config.action!=='created'?'设置配参项':'新建配参项'">
                <el-form label-width="100px" :model="config" ref="showSetupDlgForm">
                    <el-tabs v-model='activeTab' value='jsonvalue' @tab-click='handleTabClick'>
                        <el-tab-pane id='jsonvalue-pane' label="配参数值" name="jsonvalue">
                            <el-row>
                                <el-col :span="6">
                                    &nbsp;<el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <el-link size='mini' plain style="font-size: 12px;" type="primary" @click="handleValidateByUser('validate')">校验数值</el-link>
                                        <div slot="content">
                                            校验检查此配参数值是否符合格式及内容的约定（无错误提示则符合）。
                                        </div>
                                    </el-tooltip>
                                </el-col>
                                <el-col :span="6">
                                    &nbsp;<el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <el-link size='mini' plain style="font-size: 12px;" type="primary" @click="config.item_schema.default = config.item_value;">选作默认值</el-link>
                                        <div slot="content">
                                            把当前数值存储到默认值字段，以便于数值管理和恢复（简单操作无提示）。
                                        </div>
                                    </el-tooltip>
                                </el-col>
                                <el-col :span="6">
                                    <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <el-checkbox :disabled="/^boolean$|^integer$|^number$/.test(config.item_schema.type) || config.item_schema.readOnly"
                                            type="checkbox" :value="config.item_status == 2" @change='handleCipherSwitch'
                                            label="密文形式保存"></el-checkbox>
                                        <div slot="content">
                                            保存用<span style="font-weight: bold;color: red;">密文</span>到数据源/属性文件，系统获取到为<span
                                                style="font-weight: bold;color: red;">明文</span>。密文来自明文加密，不能手工直接编辑和修改。
                                        </div>
                                    </el-tooltip>
                                </el-col>
                                <el-col :span="6">
                                    <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <el-checkbox :disabled="/^boolean$|^integer$|^number$/.test(config.item_schema.type) || config.item_schema.readOnly"
                                            type="checkbox" v-model="enableSingleLineOfValue" label="无换行(\n)存储"></el-checkbox>
                                        <div slot="content">
                                            以单行紧凑的形式展示配参数值，节省空间、快速查看、有助于放入properties文件。
                                        </div>
                                    </el-tooltip>
                                </el-col>
                            </el-row>

                            <span v-if='messageOfValidateByUser' style="font-size: 12px;"><br />错误信息：<span style="color:red">{{messageOfValidateByUser}}。</span></span>
                            <span v-if='config.item_schema.readOnly' style="font-size: 12px;">修改前需<span style="color:red">关闭[只读]</span>(配参设置)。</span>
                            <span v-if='/^boolean$|^integer$|^number$/.test(config.item_schema.type)' style="font-size: 12px;">string/array/object类型支持<span
                                    style="color:red">密文和无换行(\n)紧凑</span>存储。</span>

                            <div v-if="config.item_schema.enum">
                                <br />
                                <el-select clearable :disabled="config.item_schema.readOnly" v-model="config.item_value"
                                    placeholder="请选择配参数值" style='width: 100%;'>
                                    <el-option v-for="item in config.item_schema.enum" :key="item" :label="item.toString()"
                                        :value="item">
                                    </el-option>
                                </el-select>
                            </div>
                            <hc-lang-editor v-if="!config.item_schema.enum" :readonly="config.item_schema.readOnly || (config.item_status & 2) !== 0"
                                :minlines='20' :maxlines='30' v-model="jsonTextOfValue">
                            </hc-lang-editor>

                        </el-tab-pane>

                        <el-tab-pane id="basicschema-pane" label="配参设置" name="basicschema">
                            <el-form-item id='profile-setupDlg' size='small' label="隶属环境" prop="item_profile" :rules="[ 
                                    { required: true, message: '请输入环境名称', trigger: 'blur' },
                                    { min: 2, max: 64, message: '长度在 2 到 64 个字符', trigger: 'blur' }]">
                                <el-input type="textarea" :disabled="true" :value="config.item_profile" rows="1">
                                </el-input>
                            </el-form-item>
                            <el-form-item id='code-setupDlg' size='small' label="配参代码" prop="item_code" :rules="[ 
        { required: true, message: '请输入配参代码', trigger: 'blur' },
        { min: 2, max: 64, message: '长度在 2 到 64 个字符', trigger: 'blur' }]">
                                <el-row>
                                    <el-col :span="config.action == 'created'?20:24">
                                        <el-input type="text" :disabled="config.action!=='created'" v-model="item_code_keyword"
                                            placeholder="唯一的配参代码,供程序使用" rows="1">
                                            <template v-if="item_code_prepend" slot="prepend">{{item_code_prepend}}</template>
                                        </el-input>
                                    </el-col>
                                    <el-col :span="4" v-if="config.action == 'created'">
                                        &nbsp;<el-tooltip content="Bottom center" placement="bottom" effect="light">
                                            <el-link @click='handleInspectToLoad' style="font-size: 12px;" type="primary">装载</el-link>
                                            <div slot="content">
                                                尝试从配置文件等装载配参当前数值(注：若需要格式等信息，请使用[查看并装载]工具。
                                            </div>
                                        </el-tooltip>
                                    </el-col>
                                </el-row>
                            </el-form-item>
                            <el-form-item id='name-setupDlg' size='small' label="配参名称" prop="item_name" :rules="[ 
        { required: true, message: '请输入配参名称', trigger: 'blur' },
        { min: 2, max: 64, message: '长度在 2 到 64 个字符', trigger: 'blur' }]">
                                <el-input type="textarea" v-model="config.item_name" placeholder="简明扼要的配参名字" rows="1">
                                </el-input>
                            </el-form-item>

                            <el-form-item id='options-setupDlg' size='small' prop="item_schema_options">
                                <el-row size='small' style='line-height: 30px;'>
                                    <el-col :span="8">
                                        <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                            <el-checkbox type="checkbox" :value="config.item_schema.$required" @change="($event)=>this.$set(this.config.item_schema,'$required',$event)"
                                                label="必需"></el-checkbox>
                                            <div slot="content">
                                                配参必需提供，不能为空。
                                            </div>
                                        </el-tooltip>
                                    </el-col>
                                    <el-col :span="8">
                                        <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                            <el-checkbox type="checkbox" v-model="config.item_schema.readOnly" label="只读"></el-checkbox>
                                            <div slot="content">
                                                只读时不能修改，关闭后才能进行修改，以保护安全。
                                            </div>
                                        </el-tooltip>
                                    </el-col>
                                    <el-col :span="8">&nbsp;

                                    </el-col>
                                </el-row>
                            </el-form-item>

                            <el-form-item id='default-setupDlg' size='small' label="默认数值" prop="item_schema.default"
                                :rules=" { validator:(x,y,z)=>handleValueValidate(x,config,z,true),trigger:'blur'}">
                                <el-col :span="20">
                                    <el-input v-if="!config.item_schema.enum" type="textarea" v-model="config.item_schema.default"
                                        placeholder="配参默认数值" rows="1">
                                    </el-input>

                                    <el-select clearable v-if="config.item_schema.enum" v-model="config.item_schema.default"
                                        placeholder="请选择配参默认值" style='width: 100%;'>
                                        <el-option v-for="item in config.item_schema.enum" :key="item" :label="item.toString()"
                                            :value="item">
                                        </el-option>
                                    </el-select>

                                </el-col>
                                <el-col :span="4">
                                    &nbsp;<el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <el-link :disabled="config.item_schema.readOnly" style="font-size: 12px;" type="primary"
                                            @click="config.item_value = config.item_schema.default;">使用</el-link>
                                        <div slot="content">
                                            把当前默认值赋值给配参数值。
                                        </div>
                                    </el-tooltip>

                                </el-col>
                            </el-form-item>
                            <el-form-item id='type-setupDlg' size='small' label="配参类型" width="100%">
                                <el-select v-model="config.item_schema.type" @change="handleTypeChanged" placeholder="请选择"
                                    style='width: 100%;'>
                                    <el-option value='string' label='string'>
                                    </el-option>
                                    <el-option value='boolean' label='boolean'>
                                    </el-option>
                                    <el-option value='integer' label='integer'>
                                    </el-option>
                                    <el-option value='number' label='number'>
                                    </el-option>
                                    <el-option value='array' label='array'>
                                    </el-option>
                                    <el-option value='object' label='object'>
                                    </el-option>
                                </el-select>
                            </el-form-item>

                            <el-form-item id='format-setupDlg' size='small' v-if="config.item_schema.type=='string'"
                                label="内置格式" width="100%">
                                <el-row>
                                    <el-col :span="20">
                                        <el-select v-model="config.item_schema.format" placeholder="请选择" style='width: 100%;'>
                                            <el-option value='uri' label='uri'>
                                            </el-option>
                                            <el-option value='email' label='email'>
                                            </el-option>
                                            <el-option value='hostname' label='hostname'>
                                            </el-option>
                                            <el-option value='ipv4' label='ipv4'>
                                            </el-option>
                                            <el-option value='ipv6' label='ipv6'>
                                            </el-option>
                                            <el-option value='uuid' label='uuid'>
                                            </el-option>
                                            <el-option value='date' label='date(eg.2018-11-13)'>
                                            </el-option>
                                            <el-option value='date-time' label='date-time(eg.2018-11-13T20:20:39+00:00)'>
                                            </el-option>
                                            <el-option value='time' label='time(eg.20:20:39+00:00)'>
                                            </el-option>
                                        </el-select>
                                    </el-col>
                                    <el-col :span="4">
                                        &nbsp;<el-tooltip content="Bottom center" placement="bottom" effect="light">
                                            <el-link style="font-size: 12px;" type="primary" @click="config.item_schema.format='';delete config.item_schema.format;">清除</el-link>
                                            <div slot="content">
                                                清除已经选择内置格式。
                                            </div>
                                        </el-tooltip>


                                    </el-col>
                                </el-row>

                            </el-form-item>

                            <el-form-item id='pattern-setupDlg' size='small' v-if="config.item_schema.type=='string'"
                                label="文本模式" width="100%">
                                <el-input type="textarea" v-model="config.item_schema.pattern" placeholder="js正则表达式"
                                    rows="1">
                                </el-input>
                            </el-form-item>

                            <el-form-item id='maxin-length-setupDlg' size='small' v-if="config.item_schema.type=='string'"
                                label="长度限制" width="100%">
                                <el-row>
                                    <el-col :span="2" style='font-size: 12px;'>最短</el-col>
                                    <el-col :span="10">
                                        <el-input-number size="mini" v-model="config.item_schema.minLength"></el-input-number>
                                    </el-col>
                                    <el-col :span="2" style='font-size: 12px;'>最长</el-col>
                                    <el-col :span="10">
                                        <el-input-number size="mini" v-model="config.item_schema.maxLength"></el-input-number>
                                    </el-col>
                                </el-row>
                            </el-form-item>

                            <el-form-item id='maxin-num-setupDlg' size='small' v-if="config.item_schema.type=='integer' || config.item_schema.type=='number'"
                                label="大小限制" width="100%">
                                <el-row>
                                    <el-col :span="2" style='font-size: 12px;'>最小</el-col>
                                    <el-col :span="10">
                                        <el-input-number size="mini" v-model="config.item_schema.minimum"></el-input-number>
                                    </el-col>
                                    <el-col :span="2" style='font-size: 12px;'>最大</el-col>
                                    <el-col :span="10">
                                        <el-input-number size="mini" v-model="config.item_schema.maximum"></el-input-number>
                                    </el-col>
                                </el-row>
                            </el-form-item>

                            <el-form-item id='maxin-items-setupDlg' size='small' v-if="config.item_schema.type=='array'"
                                label="数量限制" width="100%">
                                <el-row>
                                    <el-col :span="2" style='font-size: 12px;'>最少</el-col>
                                    <el-col :span="10">
                                        <el-input-number size="mini" v-model="config.item_schema.minItems"></el-input-number>
                                    </el-col>
                                    <el-col :span="2" style='font-size: 12px;'>最多</el-col>
                                    <el-col :span="10">
                                        <el-input-number size="mini" v-model="config.item_schema.maxItems"></el-input-number>
                                    </el-col>
                                </el-row>
                            </el-form-item>

                            <el-form-item id='enums-setupDlg' size='small' v-if="config.item_schema.type==='string'"
                                label="候选值枚举" width="100%">
                                <el-input type="textarea" v-model.trim="enumContent" @change="handleEnumChanged"
                                    placeholder="以,号分割的配参数值的候选项。未提供则配参可任意内容。" rows="2">
                                </el-input>
                            </el-form-item>
                            <el-form-item id='description-setupDlg' size='small' label="配参说明">
                                <el-input type="textarea" v-model="config.item_schema.description" placeholder="当前配参说明"
                                    rows="1">
                                </el-input>
                            </el-form-item>

                        </el-tab-pane>

                        <el-tab-pane id='jsonschema-pane' label="复杂结构" name="jsonschema">
                            <span v-if='illegalJsonTextOfSchema' style="color: red;font-size: 12px;">非法的JSON文本格式，需符合JSON
                                Schema规范。注:纯字符串数值无需引号。</span>

                            <hc-lang-editor :minlines='20' :maxlines='30' v-model="jsonTextOfSchema">
                            </hc-lang-editor>

                        </el-tab-pane>
                    </el-tabs>

                    <el-form-item>
                        <br v-if="config.item_schema.enum" />
                        <el-button @click='handleRowSetup' type="primary">确 认</el-button>
                        <el-button @click='showSetupDlg=false'>取 消</el-button>
                    </el-form-item>
                </el-form>
            </el-dialog>
        </div>

        <script type="module" id="setupDlg-script">
            import {} from './lib/hc-lang-editor.js'
           
           window.vmSetupDlg = new Vue({
              el: '#setupDlg',
              data: {
                 showSetupDlg:false,
                 config:{item_schema:{}},
                 enumContent:'',
                 jsonTextOfSchema:'',
                 jsonTextOfValue:'',
                 illegalJsonTextOfSchema:false,
                 messageOfValidateByUser:'',
                 enableSingleLineOfValue:false,
                 activeTab:'jsonvalue',
                 item_code_prepend:'',
                 item_code_keyword:'',
              },
              mounted:function()
              {
                  this.$el.style.display= 'block';
              },
              watch: {
                  "item_code_keyword":{
                      handler:function(nv,ov)
                      {
                          this.config.item_code = this.item_code_prepend + nv;
                      }
                  },
                  "jsonTextOfSchema": {
                      handler: function(nv, ov) {
                          if(nv === ov ) return;
                          try {
                              let nvjson = JSON.parse(nv);
                              Object.assign(this.config.item_schema,nvjson);
                              this.illegalJsonTextOfSchema = false;
                          } catch (ex) {
                              this.illegalJsonTextOfSchema = true;
                          }
                      },
                  },
                  "jsonTextOfValue": {
                      handler: function(nv, ov) {
                          if(nv === ov ) return;
                          this.config.item_value = nv;
                      },
                  },
                  "enableSingleLineOfValue":{
                      handler:function(nv,ov)
                      {
                           if(!this.jsonTextOfValue.trim().startsWith('{') && !this.jsonTextOfValue.trim().startsWith('['))
                           {
                               if(nv)
                                 this.jsonTextOfValue = this.jsonTextOfValue.trim().split('\n').join('');
                               else this.jsonTextOfValue = this.jsonTextOfValue.trim().split(',').join(',\n');
                               
                               return;
                           }
                           
                           try {
                               let nvjson = JSON.parse(this.jsonTextOfValue);
                               this.jsonTextOfValue = nv?JSON.stringify(nvjson):JSON.stringify(nvjson,null,'\t');
                               this.messageOfValidateByUser = '';
                           } catch (ex) {
                               this.messageOfValidateByUser = ex;
                           }//end of try;
                      } // end of handler
                  },
              },// end of watch
              
              methods: {
                  handleInspectToLoad:function()
                  {
                      let self = this;
                      axios.get(window.hosturl +`/inspect?code=${self.config.item_code}`).then(resp=>{
                              if(!resp.data.error)
                              {
                                  let detail = resp.data.data;
                                  if(detail.definedList.length == 0) {
                                      self.$message({showClose:true,type:"warning",message:`没有发现配参定义。`});
                                      return;
                                  }
                                  self.config.item_value = detail.definedList[0].propValue;
                                  self.$set(self.config.item_schema,"default",self.config.item_value);
                              }else self.$message({showClose:true,type:"error",message:resp.data.data});
                          }).catch(ex=>self.$message({showClose:true,type:"error",message:ex}))
                  },
                  handleCipherSwitch:function(val)
                  {
                      this.config.item_status = val?2:0;
                      let action = val?'encrypt':'decrypt';
                      let self = this;
                      axios.post(window.hosturl +'/cipher',{action:action,content:self.jsonTextOfValue})
                      .then(resp =>{
                                  let ret = resp.data.data;
                                  self.jsonTextOfValue = resp.data.error?ret:ret.result;
                            }
                         ).catch(ex=>{
                          self.$message({showClose:true,type:"error",message:ex});
                      });
                      
                  },
                  
                  handleTabClick:function(tab,event)
                  {
                      if(this.activeTab === 'jsonschema')
                      {
                          let txt = JSON.stringify(this.config.item_schema, null, '\t');
                          if(txt !== this.jsonTextOfSchema)
                           {
                               this.jsonTextOfSchema = txt;
                           }
                      }else if (this.activeTab === 'jsonvalue')
                      {
                          this.jsonTextOfValue=this.config.item_value;
                      }
                  },
                
                   showForm:function(itemRow){
                       //initialize
                       this.jsonTextOfSchema = '';
                       this.jsonTextOfValue = itemRow.item_value;
                       this.illegalJsonTextOfSchema=false,
                       this.messageOfValidateByUser='',
                       this.enableSingleLineOfValue=false,
                       this.enumContent = this.config.item_schema.enum?this.config.item_schema.enum.join(","):"";
                       this.config = JSON.parse(JSON.stringify(itemRow, null, '\t'));
                       this.activeTab= this.config.action ==='setup'?'jsonvalue':'basicschema';
                       
                       if(this.config.action ==='created')
                       {
                           this.item_code_prepend = this.config.item_code;
                           this.item_code_keyword = '';
                       }else{
                           this.item_code_prepend = '';
                           this.item_code_keyword = this.config.item_code;;
                       }
                       
                       this.showSetupDlg = true;
                   },
                   handleRowSetup:function()
                   {
                       let self = this;
                       this.$refs['showSetupDlgForm'].validate((valid) => {
                                 if (valid) {
                                   self.handleValidateByUser('submit');
                                   if(self.messageOfValidateByUser)
                                   {
                                       self.activeTab='jsonvalue';
                                       return false;
                                   }else doHandleRowSetup();
                                 } else {
                                   return false;
                                 }
                               });
                        
                        function doHandleRowSetup()
                        {
                           if(self.config.action ==='created')
                           {
                               let lst =window.vm.configList;
                               let created = self.config;
                               let i =0;

                               while(i < lst.length && lst[i].item_profile + '+' + lst[i].item_code < created.item_profile + '+' + created.item_code)
                               {
                                   i++;
                               }
                               
                               //already in configList;
                               if(i<lst.length && lst[i].id === created.id)
                               {    
                                   if(lst[i].action ==='deleted')
                                   {
                                       Object.assign(lst[i],created);
                                       lst[i].action = 'setup';
                                   }
                                   else {
                                       self.$message({showClose:true,type:'error',message:'此配置项已经存在，被隐藏、删除但未保存，不能新建！'});
                                       return;
                                   }
                               }else lst.splice(i,0,created);
                               //todo improve the filter performance
                               window.vm.handleFilter();
                           }else{
                               let row = window.vm.configList.find(it=>it.id === self.config.id);
                               
                               if(self.config.item_name === row.item_name && JSON.stringify(self.config.item_schema) === JSON.stringify(row.item_schema))
                               {
                                   self.config.action = 'updated';
                               }else self.config.action='setup';
                               
                               Object.assign(row,self.config);
                           }
                           
                           window.vm.editedCount ++;
                           self.showSetupDlg = false;
                       }
                   },
                   
                   handleEnumChanged:function(contents)
                   {
                       if(!contents){
                           delete this.config.item_schema.enum;
                           return;
                        }
                        
                        if(typeof(contents) ==='string')
                        {
                            contents = contents.trim().split(",");
                        }
                       
                       this.config.item_schema.enum = contents; 
                   },
                   
                  handleTypeChanged:function(type)
                  {
                    if(type=='boolean') this.handleEnumChanged([true,false])//'true,false') ;
                  },
                  
                  handleValueValidate:function(rule,row,callback,isDefault)
                  {
                      let result = window.validateCfgValue(row,isDefault);
                      if(!result.valid){ 
                          callback(new Error(result.message));
                      }
                      else callback();
                  },
                  handleValidateByUser:function(source)
                  {
                      if(this.config.item_status & 2)
                      {
                          if(source === 'validate')
                                this.$message({showClose:true,type:"warning",message:"请对明文编辑及校验，完成后再加密，密文不触发校验，也不能手工修改。"});
                          return;
                      }
                      let result = window.validateCfgValue(this.config,false);
                      this.messageOfValidateByUser = result.valid?"":result.message;
                      
                      if(source === 'validate' && result.valid )
                      {
                          this.$message({showClose:true,type:"success",message:"格式合法。"});
                      }
                  }
              },//end of method
          });
          
       </script>

        <div id='downloadDlg'>
            <el-dialog width="40%" :visible.sync="showDownloadDlg" title="配参导出">
                <br />
                <el-row>
                    <el-radio v-model="downloadContent" label="configList">全部配参(easyconfig管理的所有配參)</el-radio>
                </el-row>
                <br />
                <el-row>
                    <el-radio v-model="downloadContent" label="listInProfile">当前环境配参(隶属当前环境下的所有配參)</el-radio>
                </el-row>
                <br />
                <el-row>
                    <el-radio v-model="downloadContent" label="currentList">当前展示配参(当前环境、当前分组下过滤后配参项)</el-radio>
                </el-row>
                <br />
                <el-row>
                    <span style="float: right; margin-right: 100px;">
                        <el-button @click='showDownloadDlg=false'>取 消</el-button>
                        <el-button @click='handleDownload' type="primary">开 始 下 载</el-button>
                    </span>
                </el-row>

            </el-dialog>
        </div>

        <script type="module" id="userDlg-script">
            import * as $util from './lib/util.js'
            window.vmDownload = new Vue({
               el: '#downloadDlg',
               data: {
                    showDownloadDlg:false,
                    downloadContent:"configList",
                    vmdata:{},
               },
               methods: {
                   showform(vmdata, profile='default'){
                       this.vmdata = vmdata;
                       this.profile = profile;
                       this.showDownloadDlg = true;
                   },
                   handleDownload(){
                        let list = [];
                        let profile = this.profile;
                        if(this.downloadContent==='listInProfile')
                        {
                            list = this.vmdata.configList.filter(it=>it.item_profile === profile);
                        }else if(this.downloadContent==='configList' || this.downloadContent==='currentList')
                        {
                            list = this.vmdata[this.downloadContent];
                        }
                        
                        list = JSON.parse(JSON.stringify(list, null, '\t'));
                        list.forEach(it=>{delete it.action; delete it.id;});
                        list = list.filter(it=>!it.item_schema.$hidden);
                       let text = JSON.stringify({version:1.0, source:'easyconfig',list:list},null,'\t');
                       $util.download(text,'easyconfig_export.json','text/json',this);
                       
                       this.showDownloadDlg = false;
                   }
 
               },//end of method
              
           });
                
        </script>

        <div id='upload' style="display: none;">
            <el-dialog width="70%" :visible.sync="showUploadDlg" title="上传配参">
                <el-form>
                    <el-form-item>
                        <el-upload action="https://none" :file-list="fileList" :auto-upload="false" :on-change="handleUploadChange"
                            :on-remove="handleUploadChange" accept=".json">
                            <el-button slot="trigger" size="small" type="primary">
                                <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                    <span>选取配参文件</span>
                                    <div slot="content">
                                        <span>只能上传其它服务导出的配参文件，且大小不超过3Mb</span>
                                    </div>
                                </el-tooltip>
                            </el-button>
                            <span>
                                &nbsp;&nbsp;更新范围:&nbsp;&nbsp;
                                <el-checkbox v-model="incName">名称
                                </el-checkbox>
                                <el-checkbox v-model="incValue">数值
                                </el-checkbox>
                                <el-checkbox v-model="incSchema">约束
                                </el-checkbox>

                            </span>
                            &nbsp;&nbsp;导入环境:&nbsp;&nbsp;
                            <el-select size='small' v-model.trim="targetProfile" placeholder="请选择环境">
                                <el-option label="---来源环境---" value="!all!">
                                </el-option>
                                <el-option v-for="item in args.targetProfileList" :key="item.code" :label="item.name==item.code?item.code:item.code+'-'+item.name"
                                    :value="item.code">
                                </el-option>
                            </el-select>

                        </el-upload>
                    </el-form-item>
                    <el-form-item label="">
                        <el-row>
                            <el-col :span='4'>
                                <el-select size='small' v-model.trim="sourceProfile" placeholder="请选择环境" @change='handleImportFilterInput'>
                                    <el-option label="---全部环境---" value="!all!">
                                    </el-option>
                                    <el-option v-for="item in  sourceProfileList" :key="item.code" :label="item.name==item.code?item.code:item.code+'-'+item.name"
                                        :value="item.code">
                                    </el-option>
                                </el-select>

                            </el-col>
                            <el-col :span='8'>
                                <el-input type="text" size="small" placeholder="输入关键字,定位配参项" v-model="filterInput" rows="1"
                                    @input="handleImportFilterInput">
                                </el-input>
                            </el-col>
                            <el-col :span='12'>
                                <el-button style="margin-left: 10px;" size="small" type="success" :disabled="source!='easyconfig' || multipleSelection.length === 0"
                                    @click="handleConfigUpload(true)">导入配参(保存后生效)</el-button>
                                <el-button style="margin-left: 10px;" size="small" type="success" :disabled="source!='easyconfig' || multipleSelection.length === 0"
                                    @click="handleConfigUpload(false)">导入但不关闭窗口</el-button>
                                <el-button style="margin-left: 10px;" size="small" type="success" @click="showUploadDlg=false">取消返回</el-button>
                            </el-col>
                        </el-row>
                    </el-form-item>

                    <el-table max-height="400px" border :data="filteredImportedList" style="width: 100%;margin-bottom: 20px;"
                        row-key="id" @selection-change="handleSelectionChange">
                        <el-table-column type="selection" width="55">
                        </el-table-column>

                        <el-table-column label="隶属环境" prop="item_profile" :show-overflow-tooltip="true">
                        </el-table-column>

                        <el-table-column label="配参代码" prop="item_code" :show-overflow-tooltip="true">
                        </el-table-column>

                        <el-table-column label="配参名称" prop="item_name" :show-overflow-tooltip="true">
                        </el-table-column>

                        <el-table-column label="当前数值" prop="item_value" :show-overflow-tooltip="true">
                        </el-table-column>

                        <el-table-column label="格式定义" prop="item_schema" :show-overflow-tooltip="true">
                            <template slot-scope="scope">
                                <span>{{JSON.stringify(scope.row.item_schema)}}</span>
                            </template>
                        </el-table-column>

                    </el-table>

                </el-form>
            </el-dialog>
        </div>

        <script id="upload-script" type="module">
            import * as $util from './lib/util.js'
 
     
     window.vmupload = new Vue({
        el: '#upload',
        data: {
            showUploadDlg:false,
            fileList:[],
            source:'',
            importedList:[],
            filteredImportedList:[],
            filterInput:"",
            incName:false,
            incValue:true,
            incSchema:false,
            sourceProfile:'!all!',
            sourceProfileList:[],
            targetProfile:'!all!',
            multipleSelection:[],
            args:{},
        },
          mounted:function()
          {
              this.$el.style.display= 'block';
          },
        methods: {
            showform(args)
            {
                this.args = args;
                this.targetProfile = args.targetProfile;
                this.showUploadDlg = true;
            },
            handleSelectionChange(val) {
                this.multipleSelection = val;
                // console.warn('this.multipleSelection ',this.multipleSelection );
            },
            handleImportFilterInput:function()
            {
                if(this.sourceProfile==='!all!') this.targetProfile ='!all!';
                let val = this.filterInput;
                if(!val && this.sourceProfile ==='!all!') 
                {
                    this.filteredImportedList = this.importedList;
                }
                else
                {
                    this.filteredImportedList = this.importedList.filter(
                    it=> (this.sourceProfile ==='!all!' || this.sourceProfile === it.item_profile)
                        && (it.item_code && it.item_code.indexOf(val) >= 0
                            || it.item_name && it.item_name.indexOf(val) >= 0 
                            || it.item_value && it.item_value.indexOf(val) >= 0)
                     );
                 }
            },

          handleUploadChange(file, fileList) {
              this.fileList = fileList.slice(-1);
              let self = this;
              if(this.fileList.length>0)
              {
                  $util.readAsText(this.fileList[0].raw,function(data){
                      try{
                        let jsonData = JSON.parse(data);
                        self.source = jsonData.source;
                        if(jsonData.source !=='easyconfig') {
                            self.importedList.splice(0,self.importedList.length);
                            self.$message({showClose:true,type:'warning',message:'非法配参管理文件'})
                         }
                        else {
                            // self.importedList.splice(0,self.importedList.length,...jsonData.list);
                            self.importedList = jsonData.list;
                            
                            let set = new Set();
                            self.importedList.forEach(it=>set.add(it.item_profile));
                            let sourceProfileList = [];
                            set.forEach(it=>sourceProfileList.push({code:it,name:it}));
                            self.sourceProfileList = sourceProfileList;
                            
                            // console.warn("source profile list",set);
                            
                            self.handleImportFilterInput();
                            }
                      }
                      catch(ex)
                      {
                          self.importedList.splice(0,self.importedList.length);
                          self.$message({showClose:true,type:'warning',message:'非法配参管理文件，错误详情：'+ex})
                      }
                  })
              }
              else{
                  self.importedList.splice(0,self.importedList.length);
                  self.handleImportFilterInput();
              }
          },
          handleConfigUpload:function(closeform)
          {
              let self = this;
              this.multipleSelection.forEach(cfg=>{
                  if(self.targetProfile !=='!all!') cfg.item_profile = self.targetProfile;
                  
                  let lst =self.args.targetConfigList;

                  let i = 0;
                  while(i < lst.length && lst[i].item_profile + '+' + lst[i].item_code < cfg.item_profile + '+' + cfg.item_code)
                  {
                      i++;
                  };
                  
                  if(i === lst.length || cfg.item_profile + '+' + cfg.item_code !== lst[i].item_profile + '+' + lst[i].item_code)
                  {
                      cfg.action  = "created";
                      cfg.updated_user = self.args.currentUser;
                      lst.splice(i,0,cfg);
                  }else{
                      let item = lst[i];
                      item.updated_user = self.args.currentUser;
                      
                      if(self.incValue){
                          item.item_value = cfg.item_value;
                          item.item_status = cfg.item_status;
                          item.action = "updated";
                       }
                      
                      if(self.incName){
                          item.item_name = cfg.item_name; 
                          item.action = "setup";
                      }
                      
                      if(self.incSchema){
                           item.item_schema = cfg.item_schema;
                           item.action = "setup";
                      }
                      Object.assign(cfg,item);
                  };
              });
              
              if(closeform)
                this.showUploadDlg = false;
              
              
              if(this.args.callback)
                this.args.callback(this.multipleSelection);
                
            },//end of handleConfigUpload
        },//end of method
    });
    
 </script>

        <div id='groups' style="display: none;">
            <el-dialog :visible.sync="showGroupForm" title="配参分组">
                <el-row>
                    <el-col :span='12' style="font-size:small;margin-bottom: 1.125rem;">
                        <li style="padding: 1px;">把同一用途或模块的配参集中到一起，能够简化管理；</li>
                        <li style="padding: 1px;">定义分组，指定分组的属性代码前缀，系统自动对相同代码前缀配参进行归属管理；</li>
                        <li style="padding: 1px;">对应主页面不同的配参tab页签；</li>
                    </el-col>
                    <el-col :span='12'>
                        <img src='./lib/img/groups_demo.png' width="100%" />
                    </el-col>
                </el-row>
                <el-form ref="groupsForm" :model='mockmodel'>
                    <el-table border :data="groupList" style="width: 100%;" max-height="400px" row-key="id">
                        <el-table-column label="代码前缀" prop="prefix">
                            <template slot-scope="scope">
                                <el-form-item :rules="{validator:(x,y,z)=>handleValueValidate(x,y,z,scope.row),trigger:'blur'}"
                                    :prop="'prefix'" style='width: 100%;'>
                                    <el-input v-if="editing === scope.row.id" style='width: 100%;' type="textarea" rows="1"
                                        v-model.trim="scope.row.prefix" placeholder="分組代码前缀">
                                    </el-input>
                                    <span v-else>{{scope.row.prefix}}</span>
                                </el-form-item>

                            </template>
                        </el-table-column>
                        <el-table-column label="分組名称" prop="name">
                            <template slot-scope="scope">
                                <el-form-item :rules="{validator:(x,y,z)=>handleValueValidate(x,y,z,scope.row),trigger:'blur'}"
                                    :prop="'name'" style='width: 100%;'>
                                    <el-input v-if="editing === scope.row.id" type="textarea" rows="1" v-model.trim="scope.row.name"
                                        placeholder="分組名称">
                                    </el-input>
                                    <span v-else>{{scope.row.name}}</span>
                                </el-form-item>
                            </template>
                        </el-table-column>

                        <el-table-column title="操作" width="160px">
                            <template slot="header">
                                <el-button type="success" size='mini' @click="handleGroupAppend">
                                    创建
                                </el-button>
                            </template>
                            <template slot-scope="scope">
                                <el-button size='mini' @click="triggerGroupEdit(scope.row)">
                                    <span v-if="editing === scope.row.id">展示</span><span v-else>编辑</span>
                                </el-button>
                                <el-button size='mini' @click="handleGroupDelete(scope.row)">
                                    删除
                                </el-button>

                            </template>
                        </el-table-column>
                    </el-table>

                    <el-form-item>
                        <el-button @click='handleGroupUpdate' type="primary">确 认</el-button>
                        <el-button @click='showGroupForm=false'>取 消</el-button>
                    </el-form-item>
                </el-form>
            </el-dialog>
        </div>
        <script type="module" id="groups-script">
            window.vmgroups = new Vue({
               el: '#groups',
               data: {
                   showGroupForm:false,
                   editing:null,
                   maxid:0,
                   groupList:[],
                   mockmodel:{},
               },
               mounted:function()
               {
                   this.$el.style.display= 'block';
               },
               methods: {
                   showform:function(list,callback)
                   {
                       this.groupList = list?JSON.parse(JSON.stringify(list)):[];
                       let maxid = 0
                       this.groupList.forEach(it=>it.id = ++maxid);
                       this.maxid = maxid;
                       this.callback = callback;
                       this.showGroupForm = true;
                   },
                   triggerGroupEdit:function(row)
                   {
                       let self = this;
                       if(!this.editing)
                       {
                           this.editing = row.id;
                       } else{
                           this.$refs['groupsForm'].validate((valid) => {
                                     if (valid) self.editing = 0 ; else return false;
                                   });
                       }
                   },
                   handleGroupUpdate:function()
                   {
                     if(this.callback)this.callback(this.groupList)
                     this.showGroupForm =false;
                   },
                   
                   handleGroupAppend:function()
                   {
                       let self = this;
                       this.$refs['groupsForm'].validate((valid) => {
                                 if (valid) doAction(); else return false;
                               });
                        
                        function doAction(){
                           let group = {id: ++self.maxid,prefix:'',name:''};
                           self.groupList.splice(self.groupList.length,0,group);
                           self.editing = group.id;
                        }
                   },
                   handleGroupDelete:function(row)
                   {
                       let self = this;
                       this.$confirm(`删除配参分组[${row.prefix}], 是否继续?`, '提示', {
                           cancelButtonText: '取消',
                           confirmButtonText: '确定',
                           type: 'warning',
                           center: true
                       }).then(doDelete).catch(()=>{console.log(`${row} is cancelled`);});
                       
                        function doDelete()
                        {
                           let index = self.groupList.findIndex(it=>it.id===row.id);
                           self.groupList.splice(index,1); 
                        }
                   },
                   handleValueValidate:function(rule,value,callback,row)
                   {
                       let val = row[rule.field];
                       if(!val){ 
                           callback(new Error('不能为空'));
                        };
                        
                        callback();
                   },
               },//end of method
           });
                   
        </script>


        <div id='cipher' style="display: none;">
            <el-dialog :visible.sync="showCipherForm" title="加解密工具">
                <el-card style="font-size:small;margin-bottom: 1.125rem;">
                    <li>对于一些关键如基础的数据库连接、用户密码之类，且需要第一时间加载的配参，可以使用此工具加密。动态配参已经内置加密方法。</li>
                    <li>属性文件中放置密文，同时把加密属性的代码登记到easyconfig.cipher-keys属性下(不同加密项的代码逗号分割)。</li>
                </el-card>
                <el-row>
                    <el-col :span='1'>
                        &nbsp;
                    </el-col>
                    <el-col :span='10'>
                        <div>明文信息输入：</div>
                        <el-input type="textarea" v-model="plaintext" placeholder="明文文本数据" rows="8" ref="plaintext">
                        </el-input>
                        <el-link @click="copyToClipboard('plaintext')" style="color: steelblue;">复制明文到剪贴板</el-link>

                    </el-col>
                    <el-col :span='2'>
                        <el-row>
                            &nbsp;
                        </el-row>
                        <el-row>
                            &nbsp;
                        </el-row>
                        <el-row>
                            <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                <span>
                                    <el-button size='mini' @click="handleCipher('encrypt')">
                                        &gt;&gt;
                                    </el-button>
                                </span>
                                <div slot="content">
                                    生成左侧明文的密文
                                </div>
                            </el-tooltip>
                        </el-row>
                        <el-row>
                            &nbsp;
                        </el-row>
                        <el-row>
                            <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                <span>
                                    <el-button size='mini' @click="handleCipher('decrypt')">
                                        &lt;&lt;
                                    </el-button>
                                </span>
                                <div slot="content">
                                    解密右侧的密文
                                </div>
                            </el-tooltip>
                        </el-row>
                    </el-col>
                    <el-col :span='10'>
                        <div>密文信息：</div>
                        <el-input type="textarea" v-model="ciphertext" placeholder="加密文本数据" rows="8" ref="ciphertext">
                        </el-input>
                        <el-link @click="copyToClipboard('ciphertext')" style="color: steelblue;">复制密文到剪贴板</el-link>
                    </el-col>
                    <el-col :span='1'>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="8">&nbsp;</el-col>
                    <el-col :span="8">
                        <el-button @click='showCipherForm=false' type="primary">关 闭 窗 口</el-button>
                    </el-col>
                    <el-col :span="8">&nbsp;</el-col>
                </el-row>
            </el-dialog>
        </div>
        <script type="module" id="cipher-script">
            window.vmcipher = new Vue({
               el: '#cipher',
               data: {
                   showCipherForm:false,
                   plaintext:"",
                   ciphertext:"",
               },
               mounted:function()
               {
                   this.$el.style.display= 'block';
               },
               methods: {
                   handleCipher:function(action)
                   {
                       let self = this;
                       axios.post(window.hosturl +'/cipher',{action:action,content:action==='encrypt'?this.plaintext:this.ciphertext})
                       .then(resp =>{
                               let ret = resp.data.data;
                               if(action==='encrypt')
                               {
                                   this.ciphertext = resp.data.error?ret:ret.result;
                               }else this.plaintext = resp.data.error?ret:ret.result;
                              }
                          ).catch(ex=>{
                              self.$message({showClose:true,type:"error",message:ex});
                       });
                   },
                   copyToClipboard:function(refName)
                   {
                       this.$refs[refName].focus();
                       this.$refs[refName].select();
                       if (document.execCommand('copy')) {
                           document.execCommand('copy');
                           this.$message({showClose:true,type:"success",message:"复制成功"});
                       }
                   },
               },//end of method
               computed:{
        
               },
           });
           
        </script>

        <div id='item_history' style="display: none ;">
            <el-dialog :visible.sync="showItemHistoryForm" title="属性变更历史">
                <el-row>
                    <el-col :span="16">配参{{itemRow.item_name}}变更累计<span style="font-weight:bold;color: red;">{{itemUpdatedHistory.count}}</span>次</el-col>
                    <el-col :span="8">
                        <el-button size="mini" type="primary" @click="showItemHistoryForm=false">关 闭 返 回</el-button>
                    </el-col>
                </el-row>
                <el-table max-height="400px" border :data="itemUpdatedHistory.list" style="width: 100%;margin-bottom: 20px;"
                    row-key="id">
                    <el-table-column label="#" prop="id" width='80px'>
                        <template slot-scope="scope">
                            <span>{{scope.row.id}}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="维护前内容" prop="val" :show-overflow-tooltip="true">
                        <template slot-scope="scope">
                            <span>
                                <el-tag v-if="scope.row.stat & 2">密</el-tag>
                                {{scope.row.val}}
                            </span>
                        </template>
                    </el-table-column>

                    <el-table-column label="维护动作" prop="act">
                        <template slot-scope="scope">
                            <el-tag>{{scope.row.act}}</el-tag>
                        </template>
                    </el-table-column>

                    <el-table-column label="维护人" prop="user_time">
                        <template slot-scope="scope">
                            <span>
                                <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                    <span>{{scope.row.user + '@' + scope.row.time.substring(0,10)}}
                                    </span>
                                    <div slot="content">
                                        <span>{{scope.row.user + '@' + scope.row.time}}
                                        </span>
                                    </div>
                                </el-tooltip>
                            </span>
                        </template>
                    </el-table-column>

                    <el-table-column title="操作" width='80px'>
                        <template slot="header">
                            <span>操作</span>
                        </template>
                        <template slot-scope="scope">
                            <el-button v-if="scope.row.act ==='updated' || scope.row.act ==='setup' " plain size='mini'
                                @click="handleChangeToBack(scope.row)">
                                <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                    <span>恢复</span>
                                    <div slot="content">
                                        <span>改回到此更新之前的数值，【保存配参】后生效。
                                        </span>
                                    </div>
                                </el-tooltip>
                            </el-button>
                        </template>
                    </el-table-column>
                </el-table>
            </el-dialog>
        </div>

        <script type="module" id="item_history-script">
            window.vmItemHistory = new Vue({
               el: '#item_history',
               data: {
                   showItemHistoryForm:false,
                   itemUpdatedHistory:{count:0,list:[]},
                   itemRow:{},
               },
               mounted:function()
               {
                   this.$el.style.display= 'block';
               },
               methods: {
                  showForm:function(itemRow)
                  {
                      let self = this;
                      axios.get(window.hosturl +'/item-updated-history?item_code='+itemRow.item_code)
                          .then(resp =>{
                              self.itemUpdatedHistory = resp.data.data;
                              self.showItemHistoryForm = true;
                              self.itemRow = itemRow;
                          })
                          .catch(ex=>{
                              self.$message({showClose:true,type:"error",message:ex});
                          });
                      
                  },
                  handleChangeToBack:function(history)
                  {
                      if(this.itemRow.item_value === history.val && this.itemRow.item_status === history.stat)
                      {
                          this.showItemHistoryForm = false;
                          this.$message({showClose:true,type:"success",message:"内容相同，无需恢复"})
                          return;
                      }
                      this.itemRow.item_value = history.val;
                      this.itemRow.item_status = history.stat;
                      this.itemRow.updated_user= window.context.user.username;//window.currentUser;
                      this.itemRow.action = this.itemRow.action === 'setup'?'setup':'updated';
                      window.vm.editedCount ++;
                      
                      //window.vm.handleFilter(); //fuzz if it dismiss
                      
                      this.showItemHistoryForm = false;
                  }
               },//end of method
               computed:{
        
               },
           });
        </script>

        <div id='security_password' style="display: none;">
            <el-dialog :visible.sync="showSecurityChangePassword" title="修改当前用户的密码">
                <el-form label-width="100px" :model="passinfo" ref="showSecurityChangePasswordForm">

                    <el-form-item size='small' label="当前密码" prop="oldpass" :rules="[
              { required: true, message: '用户密码,区分大小写.', trigger: 'blur' },
              { min: 2, max: 64, message: '长度在 2 到 64 个字符', trigger: 'blur' }]">
                        <el-input type="password" v-model="passinfo.oldpass" placeholder="请输入当前正在使用的密码." rows="1">
                        </el-input>
                    </el-form-item>

                    <el-form-item size='small' label="新设密码" prop="newpass" :rules="[
              { required: true, message: '用户密码,区分大小写.', trigger: 'blur' },
              { min: 2, max: 64, message: '长度在 2 到 64 个字符', trigger: 'blur' }]">
                        <el-input type="password" v-model="passinfo.newpass" placeholder="输入新设置的用户密码,区分大小写." rows="1">
                        </el-input>
                    </el-form-item>

                    <el-form-item size='small' label="密码确认" prop="confirm" :rules="[
              { required: true, message: '请重新输入用户密码,以防止手误.', trigger: 'blur' },
              { min: 2, max: 64, message: '长度在 2 到 64 个字符', trigger: 'blur' },
              {validator:handleValueValidate,trigger:'blur'}]">
                        <el-input type="password" v-model="passinfo.confirm" placeholder="请重新录入用户密码,以防止手误." rows="1">
                        </el-input>
                    </el-form-item>

                    <el-form-item>
                        <el-button @click='handleSecurityChangePassword' type="primary">确 认</el-button>
                        <el-button @click='showSecurityChangePassword=false'>取 消</el-button>
                    </el-form-item>
                </el-form>
            </el-dialog>
        </div>

        <script type="module" id="security_password-script">
            function securityChangePassword(passinfo)
      {
          axios.post(window.hosturl +'/security-change-password',passinfo).then(resp=>{
              
              window.vmSecurityPassword.$message({
                  showClose:true,
                  type:resp.data.error?'error':'success',
                  message:`${resp.data.data}`});
                  
              if(!resp.data.error)
              {
                  window.vmSecurityPassword.showSecurityChangePassword =false;
              }
                  
          }).catch(ex=>{
              window.vmSecurityPassword.$message({
                  showClose:true,
                  type:'error',
                  message:ex});
          });
      }
      window.vmSecurityPassword = new Vue({
         el: '#security_password',
         data: {
             showSecurityChangePassword:false,
             passinfo:{},
         },
         mounted:function()
         {
             this.$el.style.display= 'block';
         },
         methods: {
          showform()
          {
              this.passinfo = {};
              this.showSecurityChangePassword = true;
          },
          handleSecurityChangePassword()
          {
              let self = this;
                  this.$refs['showSecurityChangePasswordForm'].validate((valid) => {
                    if (valid) {
                       securityChangePassword(self.passinfo);
                    } else {
                      return false;
                    }
                  });
           },
           
           handleValueValidate(rule,value,callback)
           {
              if(rule.field ==='confirm')
               {
                   if(this.passinfo.newpass !== this.passinfo.confirm)
                   {
                       callback(new Error('两次输入的用户密码不一致。'))
                   }
               }
               //backend
               callback();
           },
         },//end of method
     });
  </script>

        <div id='inspectedDetail' style="display: none;">
            <el-dialog :visible.sync="showInspectedDetail" title="配参详情探查" width="80%">
                <el-row>
                    <el-col :span="10">
                        <el-input :disabled="config.id?true:false" type="textarea" v-model.trim="config.item_code"
                            placeholder="请输入要探查的参数代码,例如server.port" rows="1" @keyup.enter.native="handleInspect">
                        </el-input>
                    </el-col>
                    <el-col :span="6">
                        <el-select v-if="vmargs.context.updater=='rabbitmq'" size='small' v-model.trim="targetAppNode"
                            placeholder="请选择应用节点">
                            <el-option label="---任意节点---" value="!any!">
                            </el-option>
                            <el-option v-for="item in appNodeList" :key="item.name" :label="item.name" :value="item.name">
                            </el-option>
                        </el-select>
                        <el-button @click='handleInspect' type='primary' size="small" icon="el-icon-search">
                        </el-button>
                    </el-col>
                    <el-col :span="4" style="align-items:right;">
                        &nbsp;
                        <el-link v-if="config.id" plain type="primary" size='small' @click="handleDeleteThis">删除此配参项</el-link>
                    </el-col>
                    <el-col :span="4">
                        <el-button @click='showInspectedDetail=false' type='success' size="small">关 闭 窗 口</el-button>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="16">
                        <span v-if="detail.appNode" style="font-size: small;">数据来自应用节点：<el-tag size="mini">{{detail.appNode}}</el-tag></span>
                    </el-col>
                    <el-col :span="8">
                    </el-col>
                </el-row>
                <el-collapse :value="collapse" style="padding-bottom: 0px">
                    <el-collapse-item title="配参定义" style="padding-bottom: 0px" name="def">
                        <template slot="title">
                            <span style="font-weight: bold; font-size: 14px;">配参定义</span>
                        </template>
                        <el-table stripe border :data="detail.definedList">
                            <template slot="empty">
                                <span style="font-weight: bold; font-size: 14px;">没有发现配参定义。</span>
                            </template>
                            <el-table-column label="数值" prop="propValue" :show-overflow-tooltip="true">
                                <template slot-scope="scope">
                                    <span v-if="scope.row.id==1" style="font-weight: bold;">
                                        <el-tag size="mini" type="success">生效</el-tag>{{scope.row.propValue}}
                                    </span>
                                    <span v-else style="color: gray; font-weight: bold;"><i><del>{{scope.row.propValue}}</del></i></span>
                                </template>
                            </el-table-column>
                            <el-table-column label="来源" prop="sourceName" :show-overflow-tooltip="true">
                            </el-table-column>
                            <el-table-column label="实现" prop="sourceClass" :show-overflow-tooltip="true">
                            </el-table-column>
                        </el-table>
                    </el-collapse-item>

                    <el-collapse-item name="used">
                        <template slot="title">
                            <span style="font-weight: bold; font-size: 14px;">通过标注使用</span>
                        </template>

                        <el-table border :data="detail.usedList" :show-header="false" stripe>
                            <template slot="empty">
                                <span style="font-weight: bold; font-size: 14px;">没有在Spring应用上下文及代码注册Bean中，发现通过标注的形式使用此配参(@EasyValue、@Value、@ValueEx)。</span>
                            </template>

                            <el-table-column>
                                <template slot-scope="scope">
                                    <el-table :data="scope.row.display" :show-header="false">
                                        <el-table-column prop="label" width="120px">
                                        </el-table-column>
                                        <el-table-column prop="value" :show-overflow-tooltip="true">
                                        </el-table-column>
                                    </el-table>
                                    <el-divider>
                                        <span style="color: green;" v-if="scope.row.id!=detail.usedList.length">下一个</span>
                                        <span style="color: green;" v-else>这是底线了(＾∀＾)</span>
                                    </el-divider>
                                </template>
                            </el-table-column>
                        </el-table>

                    </el-collapse-item>

                </el-collapse>
            </el-dialog>
        </div>

        <script type="module" id="inspected-detail-script">
            window.vmInspect = new Vue({
             el: '#inspectedDetail',
             data: {
                 vmargs:{config:{},context:{}},
                 config:{},
                 detail:{},
                 targetAppNode:'!any!',
                 appNodeList:[],
                 showInspectedDetail:false,
                 collapse:[],
             },
             mounted:function()
             {
                 this.$el.style.display= 'block';
             },
             methods: {
              handleDeleteThis()
              {
                  let self = this;
                  let standby = null;
                  if(this.detail.definedList.length >=2 )
                  {
                      standby = this.detail.definedList[1].propValue;
                  }
                  
                  this.$confirm(`删除当前动态配参将会使相邻配置[${standby}]生效(保存后)，你确信要删除[${self.config.item_name}]么?`, '提示', {
                      cancelButtonText: '取消',
                      confirmButtonText: '确定',
                      type: 'warning',
                      center: true
                  }).then(doActon).catch(ex=>{});
                  
                  function doActon()
                  {
                      self.showInspectedDetail = false;
                      window.vm.configList.find(it=>it.id === self.config.id).action="deleted";
                      window.vm.handleFilter();
                      window.vm.editedCount ++;
                  }
              },
              handleInspect()
              {
                  let self = this;
                  axios.get(window.hosturl +`/inspect?code=${self.config.item_code}&node=${self.targetAppNode}`).then(resp=>{
                      if(!resp.data.error)
                      {
                          let detail = resp.data.data;
                          let id = 0;
                          detail.definedList.forEach(it=>it.id=++id);
                          detail.propertiesList.forEach(it=>it.id=++id);
                          detail.definedList.push(...detail.propertiesList);
                          
                          detail.usedList.forEach(it=>{
                              it.display=[];
                              it.display.push({
                                  label:'beanName',
                                  value:it.beanName,
                              });
                              it.display.push({
                                  label:'toString()',
                                  value:it.beanString,
                              });
                              it.display.push({
                                  label:'class',
                                  value:it.beanClass,
                              });
                              it.display.push({
                                  label:'lib',
                                  value:it.jarPath,
                              });
                              // value may be affected by error assignment
                              it.display.push({
                                  label:'数值',
                                  value:it.propValue,
                              });
                              it.display.push({
                                  label:'标注',
                                  value:it.annotation,
                              });
                              it.display.push({
                                  label:'声明',
                                  value:it.propDecl,
                              });
                          });
                          
                          id = 0;
                          detail.usedList.forEach(it=>it.id=++id);
                          
                          let collapse = [];
                          if(detail.definedList.length) collapse.push('def');
                          // if(detail.propertiesList.length) collapse.push('prop');
                          if(detail.usedList.length) collapse.push('used');
                          
                          self.detail = detail;
                          self.collapse = collapse;
                          
                      }else self.$message({showClose:true,type:"error",message:resp.data.data});
                          
                  }).catch(ex=>{
                      self.$message({showClose:true,type:"error",message:ex});
                  });
              },
              showform(vmargs)
              {
                  console.log('showform(vmargs)',vmargs);
                  if(!vmargs.config) vmargs.config = {};
                  this.config = vmargs.config;
                  this.vmargs = vmargs;
                  
                  this.targetAppNode="!any!"
                  
                  let self = this;
                  axios.get(window.hosturl +`/nodes-get`).then(resp=>{
                     if(typeof resp.data!=='object' || resp.data.error)
                     {
                         console.error(resp.data.data);
                     }else self.appNodeList = resp.data.data.list;
                   }).catch(ex=>{console.error(ex);});
                  
                  if(this.config.item_code)
                    this.handleInspect();
                  else this.detail={};
                  
                  this.showInspectedDetail = true;
              },
             },//end of method
         });
      </script>

        <div id='applicationNodes' style="display: none;">
            <el-dialog width="70%" :visible.sync="showApplicationNodesDlg" title="应用节点列表">
                <el-card style="font-size:small;margin-bottom: 1.125rem;">
                    <li>应用节点标识包括IP地址、主机名称、应用端口；
                    </li>
                    <li>启动时间是节点最后启动时间，响应时间是最后得到节点响应的时间，响应消耗是粗略的请求到得到响应并记录信息的时间。
                    </li>
                </el-card>
                <el-row>
                    <el-col :span="8">
                        <el-button size="mini" type="primary" @click="showApplicationNodesDlg=false">关 闭 返 回</el-button>
                    </el-col>
                    <el-col :span="12">
                        &nbsp;
                    </el-col>
                    <el-col :span="4">
                        <el-link size="mini" plain type="success" @click="handleGetAgain" :icon="loading?'el-icon-loading':'el-icon-refresh-left'">刷新</el-link>
                    </el-col>
                </el-row>

                <el-input size="small" placeholder="请输入内容" :readonly="true" :value="applicationNodes.lastGatheredBy"
                    style="background-color: white;">
                    <template slot="prepend">此次请求节点</template>
                </el-input>
                <el-input size="small" placeholder="请输入内容" :readonly="true" :value="new Date(applicationNodes.lastGatheredTime).toLocaleDateString() + ' ' + new Date(applicationNodes.lastGatheredTime).toLocaleTimeString()">
                    <template slot="prepend">此次请求时间</template>

                </el-input>
                <span v-if="applicationNodes.list">系统共运行了[<span style="color:red;">{{applicationNodes.list.length}}</span>]个节点，如下：</span>
                <el-table v-loading.lock="loading" max-height="400px" border :data="applicationNodes.list" style="width: 100%;margin-bottom: 20px;">
                    <el-table-column label="应用节点" prop="name" :show-overflow-tooltip="true" sortable>
                    </el-table-column>

                    <el-table-column label="启用环境" prop="profiles" :show-overflow-tooltip="true">
                        <template slot-scope="scope">
                            <span>{{scope.row.profiles}}</span>
                        </template>
                    </el-table-column>

                    <el-table-column label="启动时间" prop="uptime" :show-overflow-tooltip="true" sortable>
                        <template slot-scope="scope">
                            <span>{{new Date(scope.row.uptime ).toLocaleDateString() + ' ' + new Date(scope.row.uptime ).toLocaleTimeString()}}</span>
                        </template>
                    </el-table-column>

                    <el-table-column label="响应时间" prop="ackTime" :show-overflow-tooltip="true" sortable>
                        <template slot-scope="scope">
                            <span>{{new Date(scope.row.ackTime ).toLocaleDateString() + ' ' + new Date(scope.row.ackTime ).toLocaleTimeString()}}</span>
                        </template>
                    </el-table-column>

                    <el-table-column label="响应消耗" prop="ackElapsed" width="100px" sortable>
                        <template slot-scope="scope">
                            <span>{{Number(scope.row.ackUsed).toFixed(2)}}(ms)</span>
                        </template>
                    </el-table-column>
                </el-table>
            </el-dialog>
        </div>

        <script type="module" id="appliction-nodes-script">
            window.vmApplicationNodes = new Vue({
        el: '#applicationNodes',
        data: {
            showApplicationNodesDlg: false,
            applicationNodes:{},
            checkCount : 2,
            checkingTick:2,
            loading:true,
        },
        mounted:function()
        {
            this.$el.style.display= 'block';
        },
        methods: {
            handleGetAgain(){
                let self = this;
                self.checkingTick = self.checkCount;
                self.nodesGet();
            },
            showform()
            {
               let self = this;
               axios.get(window.hosturl +`/nodes-gather`).then(resp=>{
                   if(typeof resp.data!=='object' || resp.data.error)
                   {
                       self.$message({showClose:true,type:"error",message:resp.data});
                       return;
                   }
                   self.checkingTick = self.checkCount;
                   self.nodesGet();
                   self.showApplicationNodesDlg = true;
                 
               }).catch(ex=>{
                   self.$message({showClose:true,type:"error",message:ex});
               });
           },
           nodesGet()
           {
               let self = this;
               self.loading = true;
               axios.get(window.hosturl +`/nodes-get`).then(resp=>{
                   if(typeof resp.data!=='object' || resp.data.error)
                   {
                       self.loading = false;
                       self.$message({showClose:true,type:"error",message:resp.data});
                       return;
                   }
                   let nodes = resp.data.data;
                   self.checkingTick =  self.applicationNodes.list && self.applicationNodes.list.length === nodes.list.length ? self.checkingTick -1: self.checkCount;
                   self.applicationNodes = nodes;
                   
                   if(self.checkingTick > 0)
                   {
                       setTimeout(self.nodesGet,100);
                   }
                   else self.loading = false;
                   
               }).catch(ex=>{
                   self.loading = false;
                   self.$message({showClose:true,type:"error",message:ex});
               });
           }
        },//end of method
    });
 </script>

        <div id='gray-publish' style="display: none;">
            <el-dialog width="70%" :visible.sync="showGrayPublishDlg" title="应用发布列表">
                <el-row>
                    <el-col :span="8">
                        <el-button size="mini" :disabled="this.selectedCount == 0 " type="primary" @click="handleGrayPublish(false)">发布到选中节点</el-button>
                        <el-checkbox size="mini" v-model="donotClose">未完成不关闭窗口</el-checkbox>
                    </el-col>
                    <el-col :span="8">
                        &nbsp;<el-link size="mini" icon="el-icon-scissors" type="primary" plain @click="handleGrayPublish(true)">强行结束发布</el-link>
                    </el-col>
                    <el-col :span="8">
                        <el-button size="mini" type="success" @click="showGrayPublishDlg=false">关 闭 窗口 返 回</el-button>
                        <el-link size="mini" plain type="success" @click="handleGetAgain" :icon="loading?'el-icon-loading':'el-icon-refresh-left'">刷新</el-link>
                    </el-col>
                </el-row>
                <el-table v-loading.lock="loading" max-height="400px" border :data="changeSummary.nodes" style="width: 100%;margin-bottom: 20px;">
                    <el-table-column prop="name" label="应用节点" width='200px' :show-overflow-tooltip="true">
                        <template slot-scope="scope">
                            <el-checkbox :disabled="scope.row.time?true:false || !scope.row.need" v-model="scope.row.selected"
                                @change="selectedCount += $event?1:-1;">{{scope.row.name}}</el-checkbox>
                        </template>
                    </el-table-column>
                    <el-table-column prop="profiles" label="应用环境" :show-overflow-tooltip="true">
                        <template slot-scope="scope">
                            <span>{{scope.row.profiles}}</span>
                        </template>
                    </el-table-column>
                    <el-table-column prop="ackTime" label="最后响应时间" width='200px'>
                        <template slot-scope="scope">
                            <span v-if="scope.row.ackTime">{{new Date(scope.row.ackTime).toLocaleDateString() + ' ' + new Date(scope.row.ackTime ).toLocaleTimeString()}}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="发布时间" prop="time" width='200px'>
                        <template slot-scope="scope">
                            <span>{{scope.row.time?new Date(scope.row.ackTime).toLocaleDateString() + ' ' + new Date(scope.row.ackTime ).toLocaleTimeString():""}}</span>
                        </template>
                    </el-table-column>
                </el-table>
            </el-dialog>
        </div>

        <script type="module" id="gray-publish-script">
            window.vmGrayPublish = new Vue({
               el: '#gray-publish',
               data: {
                   showGrayPublishDlg: false,
                   applicationNodes:{},
                   change:{},
                   changeSummary:{},
                   checkCount : 2,
                   checkingTick:2,
                   loading:true,
                   selectedCount:0,
                   callback:null,
                   donotClose:true,
               },
               mounted:function()
               {
                   this.$el.style.display= 'block';
               },
               methods: {
                   handleGrayPublish(terminated)
                   {
                       if(terminated)
                       {
                           this.$confirm(`[终止发布]会完成选中节点发布，剩余节点不再发布, 是否继续?`, '提示', {
                               cancelButtonText: '取消',
                               confirmButtonText: '确定',
                               type: 'warning',
                               center: true
                           }).then(()=>this.doGrayPublish(terminated))
                             .catch(e=>{console.log('canceled');});
                        }
                      else this.doGrayPublish(false);
                      
                    },
                     
                    doGrayPublish(terminated)
                    {
                       let lst =[];
                       let left = 0;
                       this.changeSummary.nodes.forEach(it=>{
                           if(it.time || !it.need) return;
                           if(it.selected)
                           {
                               lst.push(it.name);
                           } else left +=1;
                       });
                           
                     let status = "graying";
                     if(terminated) status = "terminated";
                     if(left == 0) status = "published";
                      
                     console.log("List to publish is ",lst);
                     
                     let self = this;
                     axios.post(window.hosturl +`/gray-publish`,{changeId:self.change.changed_id,status:status,list:lst}).then(resp=>{
                         if(typeof resp.data!=='object' || resp.data.error)
                         {
                             self.$message({showClose:true,type:"error",message:resp.data});
                             return;
                         }
                         
                         if(self.donotClose && status == 'graying')
                         {
                             let newsum = resp.data.data.changed_summary;
                             self.changeSummary.nodes.forEach(it=>{
                                 let finded = newsum.nodes.find(i=>it.name == i.name);
                                 if(finded)self.$set(it,"time",finded.time);
                             })
                         } else self.showGrayPublishDlg = false;
                         
                         if(self.callback) self.callback(status);
                         
                     }).catch(ex=>{
                         self.$message({showClose:true,type:"error",message:ex});
                     });
                   },
                   handleGetAgain(){
                       let self = this;
                       self.checkingTick = self.checkCount;
                       self.nodesGet();
                   },
                  showform(change,callback)
                  {
                      let chg = JSON.parse(JSON.stringify(change));
                      this.change = chg;
                      this.callback = callback;
                      
                      console.log(chg.changed_summary);
                      
                      if(!chg.changed_summary.nodes) chg.changed_summary.nodes = [];
                      
                      chg.changed_summary.nodes.forEach(it=>it.selected = true);
                      
                      this.changeSummary = chg.changed_summary;
                      this.selectedCount = 0;
                      this.nodesGather();
                  },
                  nodesGather()
                  {
                      let self = this;
                      axios.get(window.hosturl +`/nodes-gather`).then(resp=>{
                          if(typeof resp.data!=='object' || resp.data.error)
                          {
                              self.$message({showClose:true,type:"error",message:resp.data});
                              return;
                          }
                          self.checkingTick = self.checkCount;
                          self.nodesGet();
                          self.showGrayPublishDlg = true;
                        
                      }).catch(ex=>{
                          self.$message({showClose:true,type:"error",message:ex});
                      });
                  },
                  nodesGet()
                  {
                      let self = this;
                      self.loading = true;
                      axios.get(window.hosturl +`/nodes-get`).then(resp=>{
                          if(typeof resp.data!=='object' || resp.data.error)
                          {
                              self.loading = false;
                              self.$message({showClose:true,type:"error",message:resp.data});
                              return;
                          }
                          let nodes = resp.data.data;
                          self.checkingTick =  self.applicationNodes.list && self.applicationNodes.list.length === nodes.list.length ? self.checkingTick -1: self.checkCount;
                          self.applicationNodes = nodes;
                          
                          if(self.checkingTick > 0)
                          {
                              setTimeout(self.nodesGet,100);
                          }else{
                              self.mergeNodes();
                              self.loading = false;
                          };
                          
                      }).catch(ex=>{
                          self.loading = false;
                          self.$message({showClose:true,type:"error",message:ex});
                      });
                  },
                  
                  mergeNodes()
                  {
                      let self = this;
                      console.log('self.applicationNodes',self.applicationNodes);
                      console.log('self.changeSummary',self.changeSummary);
                      let profiles = self.change.changed_summary.profiles;

                      self.applicationNodes.list.forEach(node=>{
                          let existed = self.changeSummary.nodes.find(it=>it.name === node.name);
                          let need = profiles.filter(it=>node.profiles.includes(it));
                          need = need.length>0 ? true:false;
                          if(existed)
                           {
                               self.$set(existed,"ackTime",node.ackTime);
                               self.$set(existed,"profiles",node.profiles);
                               self.$set(existed,"need",need);
                               return;
                            }
                          
                          let lst =self.changeSummary.nodes;
                          lst.splice(0,0,{name:node.name,profiles:node.profiles,ackTime:node.ackTime,selected:false,need:need})
                          
                          
                      });
                    self.changeSummary.nodes.sort((a,b)=>a.name.localeCompare(b.name));
                      
                  }
               },//end of method
           });
        </script>

        <div id='application-profiles' style="display: none;">
            <el-dialog width="80%" :visible.sync="showProfilesForm" title="配参环境设置">
                <el-row>
                    <el-col :span='24' style="font-size:small;margin-bottom: 1.125rem;">
                        <el-tooltip content="Bottom center" placement="bottom" effect="light">
                            <i class="el-icon-question">help</i>
                            <div slot="content">
                                <li style="color: darkgreen;">删除环境只影响管理可见性，不会删除数据，也不影响属性生效，可加回。</li>
                                <li style="color: darkgreen;">上下移动环境位置只影响展示，不影响环境生效次序(easyconfig.profiles.active)。</li>
                            </div>
                        </el-tooltip>
                        <el-tooltip v-if="context.updater==='rabbitmq'" content="Bottom center" placement="bottom"
                            effect="light">
                            <el-button size='mini' style="font-size:x-small;margin-bottom: 1.125rem;" type="success"
                                @click="handleLoadFromNode">从节点中探测环境
                            </el-button>
                            <div slot="content">
                                <li style="color: darkgreen;">探测系统运行的应用节点，以及它们使用的环境(profile)。</li>
                                <li style="color: darkgreen;">标识环境在节点中的生效次序。</li>
                            </div>
                        </el-tooltip>
                    </el-col>
                </el-row>

                <el-form ref="profilesForm" :model='mockmodel'>
                    <el-table v-loading="loading" ref="profileList" border :data="profileList" style="width: 100%;"
                        max-height="400px" row-key="id" :highlight-current-row="true">
                        <el-table-column label="环境代码" prop="code">
                            <template slot-scope="scope">
                                <el-form-item :rules="{validator:(x,y,z)=>handleProfileValueValidate(x,y,z,scope.row),trigger:'blur'}"
                                    :prop="'code'" style='width: 100%;'>
                                    <el-input v-if="editing === scope.row.id" style='width: 100%;' type="textarea" rows="1"
                                        v-model.trim="scope.row.code" placeholder="环境代码">
                                    </el-input>
                                    <span v-else>{{scope.row.code}}</span>
                                </el-form-item>
                            </template>
                        </el-table-column>
                        <el-table-column label="环境名称" prop="name">
                            <template slot-scope="scope">
                                <el-form-item :rules="{validator:(x,y,z)=>handleProfileValueValidate(x,y,z,scope.row),trigger:'blur'}"
                                    :prop="'name'" style='width: 100%;'>
                                    <el-input v-if="editing === scope.row.id" type="textarea" rows="1" v-model.trim="scope.row.name"
                                        placeholder="环境名称">
                                    </el-input>
                                    <span v-else>{{scope.row.name}}</span>
                                </el-form-item>
                            </template>
                        </el-table-column>

                        <el-table-column label="备注" :show-overflow-tooltip="true">
                            <template slot-scope="scope">
                                <span style="font-size:x-small;margin-bottom: 1.125rem;">{{scope.row.nodes}}</span>
                            </template>
                        </el-table-column>

                        <el-table-column title="操作" width="300px">
                            <template slot="header">
                                <el-button type="success" size='mini' @click="handleProfileAppend">
                                    创建
                                </el-button>
                            </template>
                            <template slot-scope="scope">
                                <el-button size='mini' @click="triggerProfileEdit(scope.row)">
                                    <span v-if="editing === scope.row.id">查看</span><span v-else>编辑</span>
                                </el-button>
                                <el-button size='mini' @click="handleProfileDelete(scope.row)">
                                    删除
                                </el-button>
                                <el-link size="mini" type="primary" icon="el-icon-bottom icon_class" @click="moveDown(scope.$index,scope.row)">下移</el-link>
                                &nbsp; <el-link size="mini" type="primary" icon="el-icon-top icon_class" @click="moveUp(scope.$index,scope.row)">上移</el-link>
                            </template>
                        </el-table-column>
                    </el-table>

                    <el-form-item>
                        <el-button @click='handleProfileUpdate' type="primary">确 认</el-button>
                        <el-button @click='showProfilesForm=false'>取 消</el-button>
                    </el-form-item>
                </el-form>
            </el-dialog>
        </div>
        <script type="module" id="application-profiles-script">
            function nodesGather(callback, vm, checkingTick=3)
     {
         let trycount = checkingTick;
         let applicationNodes = {list:[]};
         axios.get(window.hosturl +`/nodes-gather`).then(resp=>{
             if(typeof resp.data!=='object' || resp.data.error)
             {
                 vm.$message({showClose:true,type:"error",message:resp.data.data});
                 if(callback) callback(null);
                 return;
             }
             nodesGet();
         }).catch(ex=>{
             vm.$message({showClose:true,type:"error",message:ex});
             if(callback) callback(null);
         });
         
         function nodesGet()
         {
             axios.get(window.hosturl +`/nodes-get`).then(resp=>{
                 if(typeof resp.data!=='object' || resp.data.error)
                 {
                     vm.$message({showClose:true,type:"error",message:resp.data});
                     if(callback) callback(null);
                     return;
                 }
                 let nodes = resp.data.data;
                 trycount =  applicationNodes.list.length === nodes.list.length ? trycount -1: checkingTick;
                 applicationNodes = nodes;
                 
                 if(trycount > 0)
                 {
                     setTimeout(nodesGet,100);
                 }else{
                     callback(applicationNodes);
                 };
             }).catch(ex=>{
                 vm.$message({showClose:true,type:"error",message:ex});
                 if(callback) callback(null);
             });
         }
     }
    
     window.vmApplicationProfiles = new Vue({
        el: '#application-profiles',
        data: {
            showProfilesForm:false,
            editing:null,
            loading:false,
            maxid:0,
            profileItem:{},
            profileList:[],
            mockmodel:{},
            context:{},
        },
        mounted:function()
        {
            this.$el.style.display= 'block';
        },
        methods: {
            moveUp(index, row) {
              var that = this
              if (index > 0) {
                const upDate = that.profileList[index - 1]
                that.profileList.splice(index - 1, 1)
                that.profileList.splice(index, 0, upDate)
                this.$refs.profileList.setCurrentRow(row);
              } else {
                that.$message({showClose:true,type:"warning",message:'已经是第一条，不可上移'});
              }
            },
            moveDown(index, row) {
              var that = this
              if ((index + 1) === that.profileList.length) {
                that.$message({showClose:true,type:"warning",message:'已经是最后一条，不可下移'});
              } else {
                const downDate = that.profileList[index + 1]
                that.profileList.splice(index + 1, 1)
                that.profileList.splice(index, 0, downDate)
                this.$refs.profileList.setCurrentRow(row);
              }
            },
            showform:function(profileItem,callback)
            {
                this.profileItem = profileItem;
                this.profileList = JSON.parse(profileItem.item_value).list;
                let maxid = 0
                this.profileList.forEach(it=>it.id = ++maxid);
                this.maxid = maxid;
                this.callback = callback;
                this.context = window.context;
                this.showProfilesForm = true;
            },
            triggerProfileEdit:function(row)
            {
                let self = this;
                if(!this.editing)
                {
                    this.editing = row.id;
                } else{
                    this.$refs['profilesForm'].validate((valid) => {
                              if (valid) self.editing = 0 ; else return false;
                            });
                }
            },
            handleProfileUpdate:function()
            {
              this.profileList.forEach(it=>{delete it.id;delete it.nodes;});
              let newval = JSON.stringify({list:this.profileList});
              if(this.callback && this.profileItem.item_value !== newval)
              {
                  this.profileItem.item_value = newval;
                  this.callback(this.profileItem);
              }
              this.showProfilesForm =false;
            },
            
            handleProfileAppend:function()
            {
                let self = this;
                this.$refs['profilesForm'].validate((valid) => {
                          if (valid) doAction(); else return false;
                        });
                 
                 function doAction(){
                    let profile = {id: ++self.maxid,code:'',name:''};
                    self.profileList.splice(self.profileList.length,0,profile);
                    self.editing = profile.id;
                 }
            },
            handleProfileDelete:function(row)
            {
                let self = this;
                this.$confirm(`删除配参环境[${row.code}], 是否继续?`, '提示', {
                    cancelButtonText: '取消',
                    confirmButtonText: '确定',
                    type: 'warning',
                    center: true
                }).then(doDelete).catch(()=>{console.log(`${row} is cancelled`);});
                
                 function doDelete()
                 {
                    let index = self.profileList.findIndex(it=>it.id===row.id);
                    self.profileList.splice(index,1); 
                 }
            },
            handleProfileValueValidate:function(rule,value,callback,row)
            {
                let val = row[rule.field];
                if(!val){ 
                    callback(new Error('不能为空'));
                 };
                 
                 callback();
            },
            handleLoadFromNode:function()
            {
                this.loading=true;
                this.profileList.forEach(it=>{if(it.nodes)it.nodes.splice(0,it.nodes.length)});
                nodesGather(mergeNodeProfiles,this, 3);
            }
        },//end of method
    });
         
     
     function mergeNodeProfiles(nodes)
     {
         console.warn("nodes",nodes);
         window.vmApplicationProfiles.loading=false;
         if(!nodes) return;
         
         nodes.list.forEach(node=>{
            for(let i = 0 ;i < node.profiles.length;i++)
            {
                merge(window.vmApplicationProfiles.profileList,node.profiles[i],node.name,i)
            }
         });
         
         function merge(profileList,prof,node,index)
         {
             console.warn("profileList,prof,node",profileList,prof,node);
             let profile  = profileList.find(it=>it.code==prof);
             if(profile)
             {
                 if(!profile.nodes) window.vmApplicationProfiles.$set(profile,"nodes",[node+'$'+index]);
                 else {
                     let nf = profile.nodes.find(n=>n==node);
                     if(!nf)profile.nodes.push(node+'$'+index);
                 }
             }
             else {
                 profileList.push({code:prof,name:prof,nodes:[node+'$'+index]});
             }
         }
     }          
     
 </script>

        <div id="home-bottom"></div>
    </body>
</html>
